package cn.yangliu.shiro;

import cn.yangliu.comm.CommConst;
import cn.yangliu.comm.shiro.Account;
import cn.yangliu.comm.shiro.AccountService;
import cn.yangliu.comm.shiro.PermissionService;
import cn.yangliu.comm.tools.SpringUtil;
import cn.yangliu.comm.tools.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.PrincipalCollection;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class ShiroRealm extends AuthorizingRealm {

    @Resource
    private SessionDAO sessionDAO;

    @Override
    public void setName(String name) {
        super.setName("myRealm");
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        Account account = (Account) principals.getPrimaryPrincipal();
        // 通过用户查询权限集合
        PermissionService permissionService = SpringUtil.getBean(PermissionService.class);
        List<String> permissions = permissionService                .findPermissions(account);
        Set<String> set=permissions.stream().filter(permission->
           StringUtils.isNotEmpty(permission)).collect(Collectors.toSet());
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(set);
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());
        password = new Md5Hash(password).toString();
        AccountService accountService = SpringUtil.getBean(AccountService.class);
        Account account = accountService.findAccountByUsernameAndPassword(
                username, password);
        if (account == null) {
            ShiroWebUtils.setAttribute4Request(ShiroConst.LOGIN_ERROR_KEY,
                    ShiroConst.LOGIN_ERROR_MSG);
            throw new AuthenticationException();
        }
            // 遍历session 如果当前用户存在登录,强制其他登录失效
            clearUserSession(account.getSerializableId(),ShiroWebUtils.getSession());

            accountService.updateAccountByLoginSuccess(account);
        // 从新将用户信息保存到session
        ShiroWebUtils.setAttribute4Session(ShiroConst.SESSION_USER, account);

        return new SimpleAuthenticationInfo(account, password, this.getName());
    }

    public void clearCached() {
        PrincipalCollection principals = SecurityUtils.getSubject()
                .getPrincipals();
        super.clearCache(principals);
    }

    // 踢出用户下线
    public void clearUserSession(Serializable pk,Session current) {
        CommConst.THREDPOOL.submit(()->{
            if (ShiroConst.NOT_CLEAR_PKS.contains(pk)){
                return;
            }
            Collection<Session> sessions = sessionDAO.getActiveSessions();
            for (Session session : sessions) {
                //当前遍历到的session和当前用户的session一致,则不管
                if (session.getId().equals(current.getId())){
                    continue;
                }
                Account account = (Account) session.getAttribute(ShiroConst.SESSION_USER);
                if (account != null && pk.equals(account.getSerializableId())) {
                    session.setTimeout(0);// 设置session立即失效，即将其踢出系统
                    sessionDAO.delete(session);
                    clearCached();
                    break;
                }
            }
        });

    }

}
